#include "lib/user/syscall.h"
#include "userprog/syscall.h"
#include <stdio.h>
#include "threads/malloc.h"
#include <syscall-nr.h>
#include "threads/interrupt.h"
#include "threads/thread.h"
#include "filesys/filesys.h"
#include "filesys/file.h"


/* This is a skeleton system call handler */

static void syscall_handler (struct intr_frame *);

void
syscall_init (void) 
{
  intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
}

static void
syscall_handler (struct intr_frame *f UNUSED) 
{
  //hex_dump((int)f->esp, f->esp, 0xC0000000 - (int)f->esp, true);
  
  switch(*(int*)f->esp)
  {
	  case SYS_CREATE:{
		void * filename = (f->esp+4);
		void * size = (f->esp+8);

		bool ret = filesys_create(*(int *)filename, *(unsigned *)size);
		f->eax = ret;

		break;
	  }
	  case SYS_OPEN:{
		void * filename = (f->esp+4);

		struct file * ret = filesys_open(*(int *)filename);

		if( ret == NULL ){
			f->eax = -1;
		}
		else{
			struct thread * current_thread = thread_current();
			struct myFile * tmp = (struct myFile *)malloc(sizeof(struct myFile));
			tmp->myFile = ret;
			tmp->key = current_thread->max_key;
			hash_insert(&current_thread->myFiles, &tmp->hash_elem);
			f->eax = current_thread->max_key++;
		}

		break;
	  }
	  case SYS_READ:{
		void * fd = (f->esp+4);
		void * buffer = (f->esp+8);
		void * size = (f->esp+12);
		int i;

		if( *(int *)fd == 0 ){
			for (i = 0; i < *(int *)size; i++){
				((uint8_t *)buffer)[i] = input_getc ();
			}
			f->eax = *(int *)size;
		}
		else{
			struct thread * current_thread = thread_current();
			struct myFile * cur_myFile = myFile_lookup(&current_thread->myFiles, *(int *)fd);
			struct file * cur_file = cur_myFile->myFile;
			int ret = file_read(cur_file, buffer, *(int *)size);
			f->eax = ret;
		}
		
		break;
	  }
	  case SYS_WRITE:{
		void * fd = (f->esp+4);
		void * buffer = (f->esp+8);
		void * size = (f->esp+12);

		if( *(int *)fd == 1 ){
			putbuf(*(int *)buffer, *(int *)size);
			f->eax = *(int *)size;
		}
		else{
			struct thread * current_thread = thread_current();
			struct myFile * cur_myFile = myFile_lookup(&current_thread->myFiles, *(int *)fd);
			struct file * cur_file = cur_myFile->myFile;
			int ret = file_write(cur_file, buffer, *(int *)size);
			f->eax = ret;
		}
	
		break;
	  }
	  case SYS_SEEK:{
		void * fd = (f->esp+4);
		void * position = (f->esp+8);
		
		struct thread * current_thread = thread_current();
		struct myFile * cur_myFile = myFile_lookup(&current_thread->myFiles, *(int *)fd);
		struct file * cur_file = cur_myFile->myFile;

		file_seek(cur_file, *(int *)position);

		break;
	  }
	  case SYS_CLOSE:{
		void * fd = (f->esp+4);

		struct thread * current_thread = thread_current();
		struct myFile * cur_myFile = myFile_lookup(&current_thread->myFiles, *(int *)fd);
		struct file * cur_file = cur_myFile->myFile;
			
		myFile_delete(&current_thread->myFiles, *(int *)fd);

		file_close(cur_file);

		free(cur_myFile);
		break;
	  }
	  case SYS_FILESIZE:{
		void * fd = (f->esp+4);

		struct thread * current_thread = thread_current();
		struct myFile * cur_myFile = myFile_lookup(&current_thread->myFiles, *(int *)fd);
		struct file * cur_file = cur_myFile->myFile;

		int ret = file_length(cur_file);

		f->eax = ret;
		break;
	  }
	  case SYS_TELL:{
		void * fd = (f->esp+4);

		struct thread * current_thread = thread_current();
		struct myFile * cur_myFile = myFile_lookup(&current_thread->myFiles, *(int *)fd);
		struct file * cur_file = cur_myFile->myFile;
	
		int ret = file_tell(cur_file);

		f->eax = ret;
		break;
	  }
	  case SYS_REMOVE:{
		void * filename = (f->esp+4);

		f->eax = filesys_remove(filename);
		break;
	  }
	  default:
		thread_exit ();
		break;
  }
  
}
